use crate::StrResult;

pub(crate) trait IMbc: Send {
    fn readrom(&self, addr: u16) -> u8;
    fn readram(&self, addr: u16) -> u8;
    fn writerom(&mut self, addr: u16, value: u8);
    fn writeram(&mut self, addr: u16, value: u8);
    fn check_and_reset_ram_updated(&mut self) -> bool;

    fn is_battery_backed(&self) -> bool;

    fn loadram(&mut self, ramdata: &[u8]) -> StrResult<()>;

    fn dumpram(&self) -> Vec<u8>;

    fn romname(&self) -> String {
        const TITLE_START: u16 = 0x134;
        const CGB_FLAG: u16 = 0x143;

        let title_size = match self.readrom(CGB_FLAG) & 0x80 {
            0x80 => 11,
            _ => 16,
        };

        let mut result = String::with_capacity(title_size as usize);

        for i in 0..title_size {
            match self.readrom(TITLE_START + i) {
                0 => break,
                v => result.push(v as char),
            }
        }

        result
    }
}

pub(crate) fn get_mbc(data: Vec<u8>, skip_checksum: bool) -> StrResult<Box<dyn IMbc + 'static>> {
    if data.len() < 0x150 {
        return Err("Rom size too small");
    }

    if !skip_checksum {
        checksum(&data)?;
    }

    match data[0x147] {
        _ => Err("Unsupported MBC type"),
    }
}

fn checksum(data: &[u8]) -> StrResult<()> {
    let mut value: u8 = 0;
    for i in 0x134..0x14D {
        value = value.wrapping_sub(data[i]).wrapping_sub(1);
    }

    match data[0x14D] == value {
        true => Ok(()),
        false => Err("Cartridge checksum is invalid"),
    }
}
